package org.dolphin.base.id.generator;

import java.io.Serializable;
import java.util.Properties;
import java.util.UUID;
import org.dolphin.base.id.transformer.CustomToStringTransformer;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.id.Configurable;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.UUIDGenerationStrategy;
import org.hibernate.id.uuid.CustomVersionOneStrategy;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.type.Type;
import org.hibernate.type.descriptor.java.UUIDTypeDescriptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class CustomUUIDGenerator implements IdentifierGenerator, Configurable {
  private static final Logger LOGGER = LoggerFactory.getLogger(CustomUUIDGenerator.class);
  private static final UUIDGenerationStrategy DEFAULT_STRATEGY = new CustomVersionOneStrategy();
  public static final String UUID_GEN_STRATEGY = "uuid_gen_strategy";
  public static final String UUID_GEN_STRATEGY_CLASS = "uuid_gen_strategy_class";
  private UUIDGenerationStrategy strategy;
  private UUIDTypeDescriptor.ValueTransformer valueTransformer;

  @Override
  public void configure(Type type, Properties prop, Dialect dialect) throws MappingException {
    strategy = (UUIDGenerationStrategy) prop.get(UUID_GEN_STRATEGY);
    if (strategy == null) {
      final String strategyClassName = prop.getProperty(UUID_GEN_STRATEGY_CLASS);
      if (strategyClassName != null) {
        try {
          final Class<?> strategyClass = ReflectHelper.classForName(strategyClassName);
          try {
            strategy = (UUIDGenerationStrategy) strategyClass.newInstance();
          } catch (Exception e) {
            LOGGER.warn("Unable to instantiate UUID generation strategy class : "
                + strategyClassName);
          }
        } catch (ClassNotFoundException ignore) {
          LOGGER.warn("Unable to locate requested UUID generation strategy class : "
              + strategyClassName);
        }
      }
    }
    if (strategy == null) {
      strategy = DEFAULT_STRATEGY;
    }
    if (UUID.class.isAssignableFrom(type.getReturnedClass())) {
      valueTransformer = UUIDTypeDescriptor.PassThroughTransformer.INSTANCE;
    } else if (String.class.isAssignableFrom(type.getReturnedClass())) {
      valueTransformer = CustomToStringTransformer.INSTANCE;
    } else if (byte[].class.isAssignableFrom(type.getReturnedClass())) {
      valueTransformer = UUIDTypeDescriptor.ToBytesTransformer.INSTANCE;
    } else {
      throw new HibernateException("Unanticipated return type ["
          + type.getReturnedClass().getName() + "] for UUID conversion");
    }
  }

  @Override
  public Serializable generate(SessionImplementor session, Object object) throws HibernateException {
    return valueTransformer.transform(strategy.generateUUID(session));
  }
}
